﻿using System.Linq;
using CommonBehaviors.Actions;
using FarmerJoe.Helpers;
using FarmerJoe.Helpers.Logging;
using FarmerJoe.Helpers.Singular.Helpers;
using FarmerJoe.Helpers.Units;
using FarmerJoe.Settings;
using Styx;
using Styx.CommonBot;
using Styx.Helpers;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;
using Extensions = FarmerJoe.Helpers.Extensions;

namespace FarmerJoe.Classes
{
    public static partial class Hunter
    {
        private static LocalPlayer Me { get { return StyxWoW.Me; } }
        private static WoWUnit CT { get { return Me.CurrentTarget; } }

        #region Specialisation Switchs
        // Determine which routine to use based on your specialisation
        //
        public static Composite CombatBehavior_SpecSwitch()
        {
            return new PrioritySelector(

                new Switch<WoWSpec>(ret => Me.Specialization,
                                                 new SwitchArgument<WoWSpec>(WoWSpec.HunterBeastMastery, CombatBehavior_Beast),
                                                 new SwitchArgument<WoWSpec>(WoWSpec.None, CombatBehavior_LowLevel),
                                                 new SwitchArgument<WoWSpec>(WoWSpec.HunterMarksmanship, CombatBehavior_Marks),
                                                 new SwitchArgument<WoWSpec>(WoWSpec.HunterSurvival, CombatBehavior_Survival)
                ));
        }

        public static Composite PullBehavior_SpecSwitch()
        {
            return new PrioritySelector(

                new Switch<WoWSpec>(ret => Me.Specialization,
                                                 new SwitchArgument<WoWSpec>(WoWSpec.HunterBeastMastery, PullBehavior_Beast),
                                                 new SwitchArgument<WoWSpec>(WoWSpec.None, CombatBehavior_LowLevel),
                                                 new SwitchArgument<WoWSpec>(WoWSpec.HunterMarksmanship, PullBehavior_Marks),
                                                 new SwitchArgument<WoWSpec>(WoWSpec.HunterSurvival, PullBehavior_Survival)
                ));
        }
        #endregion

        public static bool IsApproachingMe(this WoWUnit unit)
        {
            if (!unit.IsValid) return false;
            if (!unit.IsMoving) return false;

            if (unit.IsSafelyFacing(Me, 30) && unit.IsMoving && CT.CurrentTargetGuid == Me.Guid) return true;

            return false;
        }

        public static bool IsPetApproachingMe()
        {
            if (Me.Pet.IsSafelyFacing(Me, 30) && Me.Pet.IsMoving) return true;

            return false;
        }

        static WoWUnit BestExplosiveTrapTarget
        {
            get { return Clusters.GetBestUnitForCluster(AddsAndOthers.NearbyUnfriendlyUnits.Where(u => u.Combat && !u.IsCrowdControlled()), Clusters.ClusterType.Radius, 10f); }
        }


        public static Composite HuntersMark()
        {
            return new Decorator(ret => !CT.HasMyAura("Hunter's Mark") && CT.HealthPercent > 40,
                                 new PrioritySelector(
                                     new Decorator(ret => !TalentsAndGlyphs.HasGlyph("Marked for Death") && FpswareSettings.Instance.Hunter.HuntersMark.Contains("always"),
                                                   new Sequence(
                                                       //new Action( context => Log.Write(Colours.InformativeColour, "No Glyph, casting Hunter's Mark")),
                                                       Spells.Cast("Hunter's Mark")
                                                       ))
                                     )

                );
        }

        public static WoWUnit DoTAddTarget(string spellName, bool mustBeFacingTarget)//, params string[] checkDebuffs)
        {

            //foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>())
            foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
            {
                if (unit.Distance2DSqr > 40*40) continue;
                if (unit.HealthPercent < 30 && !unit.Elite) continue;
                if (!unit.InLineOfSpellSight) continue;
                if (!Extensions.ValidUnit(unit)) continue;
                if (!unit.IsTargetingMeOrPet && !unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                if (unit.AuraTimeLeft(spellName) > 3) continue;
                
                // Don't DoT the mob if they are crown controlled
                if (unit.HasAura("Wyvern Sting")) continue;
                if (unit.HasAura("Scatter Shot")) continue;
                if (unit.HasAura("Freezing Trap")) continue;

                // Check for any debuffs and if present skip this unit.
                //if (checkDebuffs.Any(debuff => unit.HasAura(debuff))) continue;

                if (mustBeFacingTarget)
                {
                    if (!Me.IsSafelyFacing(unit,80)) continue;
                }

                if (!unit.HasMyAura(spellName)) return unit;
            }

            return null;
        }

        private static WoWUnit _mobToScatterShot = null;
        public static WoWUnit MobToScatterShot
        {
            get
            {
                if (!Timers.Exists("Scatter Shot Mob")) {Timers.Add("Scatter Shot Mob");}
                if (!Timers.Expired("Scatter Shot Mob", 1000) && _mobToScatterShot != null && _mobToScatterShot.IsValid) return _mobToScatterShot;

                bool inBG = Battlegrounds.IsInsideBattleground;
                int scatterShotRange = TalentsAndGlyphs.HasGlyph("Scatter Shot") ? 23 : 20;
                foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
                {
                    if (unit.Distance2DSqr > scatterShotRange*scatterShotRange) continue;
                    if (unit.IsTargetingPet) continue;
                    if (!unit.Attackable) continue;
                    if (unit.HasAura("Wyvern Sting")) continue;
                    if (unit.HasAura("Freezing Trap")) continue;
                    if (unit.HasAura("Serpent Sting") && !TalentsAndGlyphs.HasGlyph("Scattering")) continue;
                    if (unit.GotTarget && !unit.IsPlayer && unit.CurrentTargetGuid != Me.Guid) continue;
                    if (Me.GotAlivePet && Me.Pet.GotTarget && Me.Pet.CurrentTargetGuid == unit.Guid) continue;
                    if (!unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                    if (unit.Guid == CT.Guid) continue;
                    if (!unit.InLineOfSpellSight) continue;

                    if (inBG)
                    {
                        if (!unit.IsPlayer) continue;
                        if (AddsAndOthers.MobsAttackingUnit(unit).Count > 0) continue;
                    }

                    Timers.Reset("Scatter Shot Mob");
                    _mobToScatterShot = unit;
                    return _mobToScatterShot;

                }
                return null;
            }
        }

        public static WoWUnit MobToDistractGlyphed
        {
            get
            {
                //int scatterShotRange = TalentsAndGlyphs.HasGlyph("Scatter Shot") ? 23 : 20;
                foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
                {
                    if (unit.Distance2DSqr > 40*40) continue;
                    if (unit.IsTargetingPet) continue;
                    if (unit.IsPlayer) continue;
                    if (unit.HasAura("Wyvern Sting")) continue;
                    if (unit.HasAura("Scatter Shot")) continue;
                    if (unit.HasAura("Freezing Trap")) continue;
                    if (unit.IsPlayer) continue;
                    if (unit.GotTarget && unit.CurrentTargetGuid != Me.Guid) continue;
                    if (Me.GotAlivePet && Me.Pet.GotTarget && Me.Pet.CurrentTargetGuid == unit.Guid) continue;
                    if (!unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                    if (unit.Guid == CT.Guid) continue;
                    
                    if (!unit.InLineOfSpellSight) continue;

                    return unit;

                }
                return null;
            }
        }


        private static WoWUnit _mobToWyvernSting = null;
        public static WoWUnit MobToWyvernSting
        {
            get
            {
                // So we don't get all spammy, only find a valid target once every second. 
                // If 1 second has NOT elapsed return _mobToWyvernSting if it is not NULL. If it IS NULL best we populate it then
                // If 1 second had elapsed since our last call find a suitable target, its highly likely the "new" target is exactly the same mob

                //Log.Write(Colours.InformativeColour, "=== finding mob to Wyvern sting");
                if (!Timers.Exists("Wyvern Sting target")) Timers.Add("Wyvern Sting target");
                if (!Timers.Expired("Wyvern Sting target", 1000) && _mobToWyvernSting != null && _mobToWyvernSting.IsValid)
                {
                    // We're here because or 2 reasons
                    // 1. _mobToWyvernSting is NULL or invalid
                    // 2. Its been 1 second (or more) since _mobToWyvernSting was populated with a valid target
                    return _mobToWyvernSting;
                }

                bool inBG = Battlegrounds.IsInsideBattleground;
                bool ignoreIfPetHasAggro = false;
                if (Me.Specialization == WoWSpec.HunterBeastMastery)
                {
                    if (FpswareSettings.Instance.Hunter.BeastWyvernStingPetAggro.Contains("ignore")) ignoreIfPetHasAggro = true;
                }
                
                foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
                {
                    if (unit.Distance2DSqr > 45*45) continue;
                    if (!unit.Attackable) continue;
                    if (!unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                    if (unit.IsTargetingPet && ignoreIfPetHasAggro) continue;
                    if (unit.HasAura("Explosive Trap")) continue;
                    if (unit.HasAura("Scatter Shot")) continue;
                    if (unit.HasAura("Freezing Trap")) continue;
                    
                    if (Me.GotAlivePet && Me.Pet.GotTarget && Me.Pet.CurrentTargetGuid == unit.Guid) continue;
                    if (unit.Guid == CT.Guid) continue;
                    if (!unit.InLineOfSpellSight) continue;

                    if (inBG)
                    {
                        if (!unit.IsPlayer) continue;
                        if (AddsAndOthers.MobsAttackingUnit(unit).Count > 0) continue;
                    }

                    Timers.Reset("Wyvern Sting target");
                    _mobToWyvernSting = unit;
                    return _mobToWyvernSting;

                }
                return null;
            }
        }

        public static WoWUnit MobToFreezingTrap
        {
            get
            {
                foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (unit.Distance2DSqr > 40*40) continue;
                    //if (unit.IsTargetingPet) continue;
                    if (unit.Guid == CT.Guid) continue;
                    if (unit.HasAura("Scatter Shot")) continue;
                    if (unit.HasAura("Wyvern Sting")) continue;
                    if (unit.HasAura("Exposive Trap")) continue;
                    if (!unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                    if (unit.IsMoving) continue;
                    if (unit.HasAura("Serpent Sting") && !TalentsAndGlyphs.HasGlyph("Icy Solace")) continue;
                    if (unit.GotTarget && unit.CurrentTargetGuid != Me.Guid) continue;
                    if (Me.GotAlivePet && Me.Pet.GotTarget && Me.Pet.CurrentTargetGuid == unit.Guid) continue;
                    
                    if (!unit.InLineOfSpellSight) continue;

                    return unit;

                }
                return null;
            }
        }

        /// <summary>
        /// Used for Disengage. If we can navigate to a point behind us then we can safely use Disengage (thats the idea).
        /// </summary>
        public static bool CanNavigateBehindMe
        {
            get
            {
                return Navigator.CanNavigateFully(StyxWoW.Me.Location, WoWMathHelper.CalculatePointBehind(StyxWoW.Me.Location, StyxWoW.Me.Rotation, 30));
            }
        }

        public static bool InAotF
        {
            get { return Me.HasAura("Aspect of the Fox"); }
        }

        public static bool InAotH
        {
            get { return Me.HasAura("Aspect of the Hawk") || Me.HasAura("Aspect of the Iron Hawk"); }
        }


        public static Composite CrowdControl()
        {
            return new PrioritySelector(
                
                // Wyvern Sting 
                    new Decorator(ret => AddsAndOthers.HasAdds && !Me.InVehicle && SpellManager.HasSpell("Wyvern Sting") && !SpellManager.Spells["Wyvern Sting"].Cooldown && MobToWyvernSting != null && SpellManager.CanCast("Wyvern Sting"),
                        new Sequence(
                            new DecoratorContinue(ret=> MobToWyvernSting.Distance2DSqr > 35*35,
                                new Sequence(
                                    new Action(context => Log.Write(Colours.InformativeColour, "Wyvern Sting target is out of range, moving closer")),
                                    Movement.MoveToTarget(true, 34f)
                                )),
                            new WaitContinue(2, ret => !SpellManager.GlobalCooldown && !Me.IsCasting, new ActionAlwaysSucceed()),
                            new Action(context => Log.Write(Colours.InformativeColour, "Wyvern Sting on add!")),
                            new Action(context => MobToWyvernSting.Face()),
                            new WaitContinue(1, ret => false, new ActionAlwaysSucceed()),
                            Spells.Buff("Wyvern Sting", ret => MobToWyvernSting, ret => SpellManager.CanCast("Wyvern Sting"), false)
                            )),

                    // Freezing Trap 
                    new Decorator(ret => !Me.InVehicle && Me.HasAura("Trap Launcher") && SpellManager.HasSpell("Freezing Trap") && !SpellManager.Spells["Freezing Trap"].Cooldown && CT.Distance < 40 && AddsAndOthers.HasAdds /*&& Timers.AutoExpire("Trap Spam", 3000)*/ && MobToFreezingTrap != null,
                        new Sequence(
                            new Action(ret => Log.Write(Colours.InformativeColour, "Freezing Trap on add")),
                            new Action(ret => SpellManager.Cast("Freezing Trap")),
                            new WaitContinue(1, ret => Me.HasPendingSpell("Freezing Trap"), new ActionAlwaysSucceed()),
                            new Action(ret => SpellManager.ClickRemoteLocation(MobToFreezingTrap.Location)),
                            new WaitContinue(1, ret => false, new ActionAlwaysSucceed())
                            )
                        ),

                        // Scatter Shot
                    new Decorator(ret => !Me.InVehicle && AddsAndOthers.HasAdds && SpellManager.HasSpell("Scatter Shot") && !SpellManager.Spells["Scatter Shot"].Cooldown && MobToScatterShot != null && SpellManager.CanCast("Scatter Shot"),
                        new Sequence(
                            new WaitContinue(2, ret => !SpellManager.GlobalCooldown && !Me.IsCasting, new ActionAlwaysSucceed()),
                            new Action(context => Log.Write(Colours.InformativeColour, "Scatter shot on add, " + MobToScatterShot.SafeName())),
                            new DecoratorContinue(ret=> !Me.IsSafelyFacing(MobToScatterShot,50),
                                new Sequence(
                                    new Action(context => MobToScatterShot.Face()),
                                    new WaitContinue(1, ret => false, new ActionAlwaysSucceed())
                                )
                            ),
                            
                            Spells.Buff("Scatter Shot", ret => MobToScatterShot, ret => SpellManager.CanCast("Scatter Shot"), false)
                            ))

                
                );
        }

        public static Composite PetSpells()
        {
            return new Decorator(ret => Me.GotAlivePet && !Me.InVehicle,
                new PrioritySelector(

                    new Decorator(ret => Timers.AutoExpire("Pet Cower",500) && CLC.ResultOK(FpswareSettings.Instance.Hunter.PetAbilityCowerWhen) && Me.Pet.HealthPercent <= FpswareSettings.Instance.Hunter.PetAbilityCowerHealth && Pets.CanCastPetAction("Cower"),
                        new Sequence(
                            new Action(context => Pets.CastPetAction("Cower")))
                    ),

                    new Decorator(ret => Timers.AutoExpire("Pet Last Stand", 500) && Me.Pet.HealthPercent <= FpswareSettings.Instance.Hunter.PetAbilityLastStandHealth && Pets.CanCastPetAction("Last Stand"),
                        new Sequence(
                            new Action(context => Pets.CastPetAction("Last Stand")))
                    ),

                    new Decorator(ret => CLC.ResultOK(FpswareSettings.Instance.Hunter.PetAbilityRabid) && Pets.CanCastPetAction("Rabid"),
                        new Sequence(
                            new Action(context => Pets.CastPetAction("Rabid")))
                    ),

                    new Decorator(ret => CLC.ResultOK(FpswareSettings.Instance.Hunter.PetAbilityRoarOfSacrifice) && Pets.CanCastPetAction("Roar of Sacrifice"),
                        new Sequence(
                            new Action(context => Pets.CastPetAction("Roar of Sacrifice",Me)))
                    )


                            ));
        }


        public static Composite SummonPet(string petSlot)
        {
            return
                new Decorator( ret => !FpswareSettings.Instance.Hunter.PetSlot.Contains("none") && !Me.GotAlivePet && !Me.Mounted && !Me.InVehicle && !Me.IsFlying && Timers.AutoExpire("Summon Pet",1500) && !Me.HasAura("Food") && !Me.HasAura("Drink"), // && !Me.IsCasting,
                    new PrioritySelector(

                        Spells.WaitForCast(),
                        
                        /*
                        new Decorator(ret => StyxWoW.Me.Pet != null,
                                      new PrioritySelector(
                                          Movement.EnsureMovementStopped(),
                                          Spells.BuffSelf("Revive Pet"))
                        ),
                         */

                        new Decorator(ret=> Me.Pet != null && Me.Pet.IsDead && Pets.CanCastPetAction("Heart of the Phoenix"),
                            new Action(context => Pets.CastPetAction("Heart of the Phoenix"))
                                ),

                        new Sequence(

                            new Action(ret => SpellManager.Cast(petSlot)),
                            new WaitContinue(1, ret => !SpellManager.GlobalCooldown, new ActionAlwaysSucceed()),
                            Spells.WaitForLagDuration(),
                            new WaitContinue(2, ret => StyxWoW.Me.GotAlivePet, new ActionAlwaysSucceed()),
                            new Decorator(ret => !StyxWoW.Me.GotAlivePet,
                                          new Action(context => SpellManager.Cast("Revive Pet")))
                        )

                        ));

            /*
                    new Decorator(ret => SpellManager.CanCast("Call Pet 1"),
                        new Sequence(
                            new Action(ret => SpellManager.Cast("Summon Imp")),
                            Spells.WaitForLagDuration(),
                            new Action(ret => Timers.Reset("Summon Pet")),
                            new Wait(2, ret => StyxWoW.Me.GotAlivePet || !StyxWoW.Me.IsCasting,
                                new PrioritySelector(
                                    new Decorator(ret => StyxWoW.Me.IsCasting,
                                        new Action(ret => SpellManager.StopCasting())),
                                        new ActionAlwaysSucceed()))
                    )))
                     */

        }


        public static Composite PreCombatBuffs()
        {
            return new PrioritySelector(

                Spells.WaitForCast(),
                

                new Decorator(ret => Me.Specialization == WoWSpec.HunterBeastMastery,
                    new PrioritySelector(
                        SummonPet(FpswareSettings.Instance.Hunter.PetSlot)

                )),

                new Decorator(ret => Me.Specialization == WoWSpec.HunterMarksmanship,
                    new PrioritySelector(
                        SummonPet("Call Pet 1")

                )),

                new Decorator(ret => Me.Specialization == WoWSpec.HunterSurvival,
                    new PrioritySelector(
                        SummonPet("Call Pet 1")

                )),



                new Decorator(ret => Me.IsCasting || Me.IsChanneling, new Action(context => RunStatus.Success))

                );
        }

        public static WoWUnit _lootableMob = null;
        public static WoWUnit LootableMob
        {
            get
            {
                // If looting is enabled set a minimum distance of 10 yards otherwise set it to 1 yard
                // This prevents the pet being sent in to loot mobs that we will get to first. 
                int minLootableDistance = LootTargeting.LootMobs ? 10*10 : 1*1;

                foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (Me.Combat || Me.Pet.Combat) continue;
                    if (unit.Distance2DSqr > 50*50) continue;
                    if (unit.Distance2DSqr < minLootableDistance) continue;
                    if (!unit.InLineOfSpellSight) continue;
                    if (unit.IsAlive) continue;
                    if (!unit.Lootable) continue;

                    //if ()
                    return unit;
                }

                return null;
            }
        }

        public static Composite PetLoot()
        {
            return new Decorator(ret=> FpswareSettings.Instance.Hunter.PetFetch.Contains("always") &&  Me.GotAlivePet && !Me.Combat && TalentsAndGlyphs.HasGlyph("Fetch") && SpellManager.HasSpell("Fetch") && !Me.IsCasting && Timers.AutoExpire("Pet Fetch",500) && Me.Pet.Distance <= 5 && LootableMob != null,
                new Sequence(
                    new DecoratorContinue(ret => Me.IsMoving, new Action(context => Navigator.PlayerMover.MoveStop())),
                    new Action(context => Log.Write(Colours.InformativeColour, "Sending pet to loot " + LootableMob.Name + ", waiting here for a few seconds")),
                    new Action(context => SpellManager.Cast("Fetch",LootableMob)),
                    new DecoratorContinue(ret => Me.IsMoving, new Action(context => Navigator.PlayerMover.MoveStop())),
                    //new DecoratorContinue(ret=> Me.IsMoving, new Action(context => Movement.EnsureMovementStopped())),
                    new WaitContinue(2, ret=> false, new ActionAlwaysSucceed()),
                    new DecoratorContinue(ret => Me.IsMoving, new Action(context => Movement.EnsureMovementStopped())),
                    //new Action(context => Log.Write(Colours.InformativeColour, "Wait until our pet is approaching us")),
                    new WaitContinue(8, ret => (Me.Pet.Distance <= 8  && (IsPetApproachingMe() || !Me.Pet.IsMoving)) || (Me.Combat || Me.Pet.Combat), new ActionAlwaysSucceed())
                    //new Action(context => Log.Write(Colours.InformativeColour, "Finished waiting"))
                    )
            );
        }



        public static Composite AllFormsRest()
        {
            return new PrioritySelector(

                Spells.WaitForCast(),
                PetLoot(),
                //Spells.BuffSelf("Create Healthstone", ret => NeedHealthStone && !Me.IsCasting && Timers.AutoExpire("Create Healthstone", 5000)),
                //Spells.BuffSelf("Dark Intent", ret => !Me.ActiveAuras.ContainsKey("Dark Intent") && FpswareSettings.Instance.Warlock.DarkIntent.Contains("always")),

                new Decorator(ret => Me.Specialization == WoWSpec.HunterBeastMastery,
                    new PrioritySelector(
                        SummonPet(FpswareSettings.Instance.Hunter.PetSlot)

                        )),

                new Decorator(ret => Me.Specialization == WoWSpec.HunterMarksmanship,
                    new PrioritySelector(
                        SummonPet("Call Pet 1")

                        )),

                new Decorator(ret => Me.Specialization == WoWSpec.HunterSurvival,
                    new PrioritySelector(
                        SummonPet("Call Pet 1")

                )),


                new Decorator(ret => Me.IsCasting || Me.IsChanneling || SpellManager.GlobalCooldown, new Action(context => RunStatus.Success)),

                // Base common behaviour
                Common.RestBehaviour()

                );
        }
    }
}
